package com.ruoyi.project.pay.weixin.app;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.project.pay.common.PayUrl;
import com.ruoyi.project.pay.weixin.modal.WxAppModal;
import com.ruoyi.project.pay.weixin.util.MD5;
import com.ruoyi.project.pay.weixin.util.SignCreateUtil;
import com.ruoyi.project.pay.weixin.util.WxToolUtil;
import com.ruoyi.project.pay.weixin.util.XMLUtil;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.jdom2.JDOMException;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * Filename: WeixinAppPay.java <br>
 *
 * Description: 微信APP支付<br>
 *
 * @author: lenovo <br>
 * @version: 1.0 <br>
 * @Createtime: 2019-12-01 <br>
 *
 * Modification History:
 * Date         Author          Version            Description
 *---------------------------------------------------------*
 * 2019-12-01      lenovo            v1.0              第一次创建
 *
 * @Copyright: Copyright (c)2019 by lenovo <br>
 *
 */
@Slf4j
public class WeixinAppPay {

    public static void main(String[] args) {
        try {
            WxAppModal modal = new WxAppModal();
            modal.setBody("测试商品");
            modal.setOut_trade_no("123456120123");
            modal.setTotal_fee("888");
            System.out.println(getOrderInfo(modal));
        } catch (IOException | JDOMException e) {
            e.printStackTrace();
        }
    }

    /**
     * @MethodName: getOrderInfo
     * @Description:  微信APP支付
     * @param modal 支付参数实体
     * @return String 支付所需参数
     * @author lenovo
     * @date 2019-12-01
     */
    public static String getOrderInfo(WxAppModal modal) throws IOException, JDOMException {
        //请求参数
        TreeMap<String, Object> arrays = new TreeMap<>();
        arrays.put("appid", modal.getAppId());
        arrays.put("body", modal.getBody());
        arrays.put("mch_id", modal.getMchId());
        arrays.put("nonce_str", WxToolUtil.getNonce_str());
        arrays.put("notify_url", modal.getNotify_url());
        arrays.put("out_trade_no", modal.getOut_trade_no());
        arrays.put("spbill_create_ip", WxToolUtil.getIpAddress());
        arrays.put("total_fee", (Double.valueOf(modal.getTotal_fee())*100+"").replace(".0",""));
        arrays.put("trade_type", "APP");
        arrays.put("attach", modal.getAttach());

        // 签名
        String sign = SignCreateUtil.createSign(modal.getSecurity(), arrays);
        // 拼接请求参数
        arrays.put("sign", sign);
        // 转换请求参数为XML
        String xml = XMLUtil.parseXML(arrays);
        // 请求预付单id
        OkHttpClient client = new OkHttpClient();
        Request requestOkhttp = new Request.Builder().url(PayUrl.URL)
                .post(RequestBody.create(MediaType.parse("application/xml"), xml)).build();
        Call call = client.newCall(requestOkhttp);
        String result = call.execute().body().string();
        //结果集
        Map<String, String> resultMap = XMLUtil.parseMap(result);
        log.info("统一下单接口返回结果" + result);
        //取得预付单id
        if (resultMap.containsKey("result_code") && resultMap.containsKey("return_code") && resultMap.containsKey("prepay_id")) {
            String return_code = resultMap.get("return_code");
            String result_code = resultMap.get("result_code");
            if ("SUCCESS".equals(result_code) && "SUCCESS".equals(return_code)) {
                //取得prepare_id
                String prepay_id = resultMap.get("prepay_id");
                log.info("prepay_id:{}",prepay_id);
                SortedMap<String, Object> orderInfoMap = new TreeMap<>();
                orderInfoMap.put("appid", modal.getAppId());
                orderInfoMap.put("partnerid", modal.getMchId());
                orderInfoMap.put("prepayid", prepay_id);
                orderInfoMap.put("noncestr", WxToolUtil.getNonce_str());
                orderInfoMap.put("package", "Sign=WXPay");
                orderInfoMap.put("timestamp", WxToolUtil.getTimestamp());
                String sign2 = SignCreateUtil.createSign(modal.getSecurity(), orderInfoMap);
                orderInfoMap.put("sign", sign2);
                return JSON.toJSONString(orderInfoMap);
            } else {
                throw new IOException("no result..");
            }
        } else {
            throw new IOException("no result..");
        }
    }

    /**
     * 验证回调签名
     *
     * @return
     */
    public static boolean isTenpaySign(Map<String, String> map) {
        String characterEncoding = "utf-8";
        String charset = "utf-8";
        String signFromAPIResponse = map.get("sign");
        if (signFromAPIResponse == null || signFromAPIResponse.equals("")) {
            System.out.println("API返回的数据签名数据不存在，有可能被第三方篡改!!!");
            return false;
        }
        log.debug("服务器回包里面的签名是:" + signFromAPIResponse);
        //过滤空 设置 TreeMap
        SortedMap<String, String> packageParams = new TreeMap();

        for (String parameter : map.keySet()) {
            String parameterValue = map.get(parameter);
            String v = "";
            if (null != parameterValue) {
                v = parameterValue.trim();
            }
            packageParams.put(parameter, v);
        }

        StringBuffer sb = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();

        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (!"sign".equals(k) && null != v && !"".equals(v)) {
                sb.append(k + "=" + v + "&");
            }
        }
        // TODO: 2019/1/25 微信支付的key
        sb.append("key=" + new WxAppModal().getSecurity());

        //将API返回的数据根据用签名算法进行计算新的签名，用来跟API返回的签名进行比较
        //算出签名
        String resultSign = "";
        String tobesign = sb.toString();

        if (null == charset || "".equals(charset)) {
            resultSign = MD5.encode(tobesign).toUpperCase();
        } else {
            try {
                resultSign = MD5.encode(tobesign).toUpperCase();
            } catch (Exception e) {
                resultSign = MD5.encode(tobesign).toUpperCase();
            }
        }

        String tenpaySign = packageParams.get("sign").toUpperCase();
        return tenpaySign.equals(resultSign);
    }

    /**
    * @MethodName: notifyWeiXinPay
    * @Description:  微信支付回调校验
    * @param request 微信返回request
    * @return String 通知微信
    * @author rongrong
    * @date 2019-12-02
    */
    public static Boolean notifyWeiXinPay(HttpServletRequest request) throws IOException, JDOMException {
        InputStream inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        String resultXml = new String(outSteam.toByteArray(), "utf-8");
        Map<String, String> params = XMLUtil.parseMap(resultXml);
        outSteam.close();
        inStream.close();

        Map<String, String> return_data = new HashMap<>();
        if (!isTenpaySign(params)) {
            // 支付失败
            log.debug("支付回调校验失败==>{}", JSONObject.toJSONString(params));
            return false;
        }
        log.info("===============付款成功==============");
        return true;
    }

}
